package com.etone.smartAudit.service;

import cn.hutool.core.collection.CollectionUtil;
import com.etone.smartAudit.domain.UploadFile;
import com.etone.smartAudit.dto.FileDTO;
import com.etone.smartAudit.error.CustomParameterizedException;
import com.etone.smartAudit.mapper.UploadFileMapper;
import com.etone.smartAudit.vo.FileVO;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author: 蒋学伟
 * @create: 2020-06-16 14:47:34
 * @Description 文件管理
 */
@Service
@Log4j2
public class FileUploadService {
    @Value("${audit.filePath}")
    private String filePath;

    @Value("${audit.allowFileExtension:}")
    private String allowFileExtension;

    @Value("${audit.baseUrl}")
    private String baseUrl;

    @Value("${server.servlet.context-path}")
    private String contextPath;

    @Value("${audit.clearLocalFile:false}")
    private Boolean clearLocalFile;

    @Autowired
    private UploadFileMapper uploadFileMapper;

    /**
     * 上传文件到本地
     *
     * @param path
     * @param type
     * @param files
     * @return
     */
    public List<FileVO> fileLocalUpload(String path, String type, MultipartFile[] files) {
        List<FileVO> fileVOS = new ArrayList<>();
        String curType = type.replaceAll("\\\\", "").replaceAll(" ", "").replaceAll("/", "");
        String filePathStr = "/" + curType;
        if (!StringUtils.isEmpty(path)) {
            path = path.replaceAll("\\\\", "").replaceAll(" ", "").replaceAll("/", "");
            filePathStr = "/" + path + filePathStr;
        }
        String flagFilePath = filePathStr;

        File curPath = new File(filePath + filePathStr);
        if (!curPath.exists()) {
            curPath.mkdirs();
        }
        Random random = new Random();
        Arrays.asList(files).forEach(file -> {
            //原文件名
            String originalFilename = file.getOriginalFilename();
            //扩展名
            String extension = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);
            //检查文件类型
            checkFileExtensionThrow(extension);
            //文件名
            String uploadName = System.currentTimeMillis() + "_" + String.format("%05d", random.nextInt(100000)) + "." + extension;
            String relativePath = flagFilePath + "/" + uploadName;
            String url = contextPath + "/upload/files" + relativePath;
            String fullUrl = url;
            File curFile = new File(filePath + relativePath);
            try {
                fileVOS.add(FileVO.builder()
                        .name(uploadName)
                        .originName(originalFilename)
                        .size(file.getSize())
                        .url(url)
                        .fullUrl(fullUrl)
                        .index(fileVOS.size() + 1)
                        .success(true)
                        .path(relativePath)
                        .extension(extension)
                        .build());
                UploadFile uploadFile = UploadFile
                        .builder()
                        .url(url)
                        .path(relativePath)
                        .createTime(new Date())
                        .extension(extension)
                        .name(uploadName)
                        .originName(originalFilename)
                        .size(file.getSize())
                        .build();
                uploadFileMapper.insert(uploadFile);

                file.transferTo(curFile);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        //同步到其他服务器，负载均衡时使用

        return fileVOS;
    }

    /**
     * 检查扩展名是否有效
     *
     * @param fileExtension
     * @return
     */
    public boolean checkFileExtension(String fileExtension) {
        if (StringUtils.isEmpty(allowFileExtension)) {
            return true;
        } else {
            String[] extensions = allowFileExtension.split(",");
            return Arrays.stream(extensions).anyMatch(extension -> fileExtension.equals(extension));
        }
    }

    /**
     * 检查扩展名是否有效,并抛异常
     *
     * @param fileExtension
     */
    public void checkFileExtensionThrow(String fileExtension) {
        if (!checkFileExtension(fileExtension)) {
            throw new CustomParameterizedException("该文件类型不允许上传");
        }
    }

    /**
     * 读取文件
     *
     * @param path
     * @param type
     * @param fileName
     * @param response
     */
    public void getFile(String path, String type, String fileName, HttpServletResponse response) {
        File file = new File(filePath + "/" + (!StringUtils.isEmpty(path) ? path + "/" : "") + type + "/" + fileName);
        getFile(file, response, fileName);
    }

    /***
     * 重载文件下载，供项目文件使用
     * @param file
     * @param response
     * @param fileName
     */
    public void getFile(File file, HttpServletResponse response, String fileName) {
        if (!file.exists()) {
            log.info("文件路劲：{}", file.getPath());
            log.info("文件不存在!");
            //如果文件不存在，则从负载的其他服务器获取，并下载到本服务器

            throw new CustomParameterizedException("文件不存在");
        }
        try {
            getFile(new FileInputStream(file), response, fileName);
        } catch (FileNotFoundException e) {
            log.info("文件读取失败：{}", e.getLocalizedMessage());
            e.printStackTrace();
        }
//        String extension = FilenameUtils.getExtension(fileName).toLowerCase();
//        if (extension.equals("jpg") || extension.equals("jpeg")) {
//            response.setContentType("image/jpeg");
//        } else if (extension.equals("png")) {
//            response.setContentType("image/png");
//        } else if (extension.equals("gif")) {
//            response.setContentType("image/gif");
//        } else {
//            response.setHeader("content-type", "application/octet-stream");
//            response.setContentType("application/octet-stream");
//        }
//        try {
//            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
//        } catch (UnsupportedEncodingException e2) {
//            e2.printStackTrace();
//        }
//        byte[] buff = new byte[1024];
//        BufferedInputStream bis = null;
//        OutputStream os = null;
//        try {
//            os = response.getOutputStream();
//            bis = new BufferedInputStream(new FileInputStream(file));
//            int i = bis.read(buff);
//            while (i != -1) {
//                os.write(buff, 0, buff.length);
//                os.flush();
//                i = bis.read(buff);
//            }
//        } catch (IOException e) {
//            e.printStackTrace();
//        } finally {
//            if (bis != null) {
//                try {
//                    bis.close();
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            }
//        }
    }

    /***
     * 获取远程文件
     * @return
     */
    private File getRemoteFile(File file, HttpServletResponse response, String fileName) {
        RequestCallback requestCallback = request -> request.getHeaders()
                .setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
        RestTemplate restTemplate = new RestTemplate();
        // 对响应进行流式处理而不是将其全部加载到内存中
        String downloadUrl = "";
        String localTempFilePath = "";
        restTemplate.execute(downloadUrl, HttpMethod.GET, requestCallback, clientHttpResponse -> {
            Files.copy(clientHttpResponse.getBody(), Paths.get(localTempFilePath));
            return null;
        });
        return null;
    }

    /***
     * 重载文件下载，供项目文件使用
     * @param inputStream
     * @param response
     * @param fileName
     */
    public void getFile(InputStream inputStream, HttpServletResponse response, String fileName) {
        String extension = FilenameUtils.getExtension(fileName).toLowerCase();
        if (extension.equals("jpg") || extension.equals("jpeg")) {
            response.setContentType("image/jpeg");
        } else if (extension.equals("png")) {
            response.setContentType("image/png");
        } else if (extension.equals("gif")) {
            response.setContentType("image/gif");
        } else {
            response.setHeader("content-type", "application/octet-stream");
            response.setContentType("application/octet-stream");
        }
        try {
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        } catch (UnsupportedEncodingException e2) {
            log.info("返回响应头错误:{}", e2.getMessage());
            e2.printStackTrace();
        }
        byte[] buff = new byte[1024];
        BufferedInputStream bis = null;
        OutputStream os = null;
        try {
            os = response.getOutputStream();
            bis = new BufferedInputStream(inputStream);
            int i = bis.read(buff);
            while (i != -1) {
                os.write(buff, 0, buff.length);
                os.flush();
                i = bis.read(buff);
            }
        } catch (IOException e) {
            log.info("流转换错误:{}", e.getMessage());
            e.printStackTrace();
        } finally {
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    log.info("流关闭错误:{}", e.getMessage());
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 删除文件
     *
     * @param fileDTOS
     * @param isDeleteLocal
     * @param isDeleteDB
     */
    public void deleteFileByFileDTO(List<FileDTO> fileDTOS, boolean isDeleteLocal, boolean isDeleteDB) {
        deleteFileByURL(fileDTOS.stream().filter(fileDTO -> fileDTO != null).map(fileVO -> fileVO.getUrl()).collect(Collectors.toList()), isDeleteLocal, isDeleteDB);
    }

    /**
     * 删除文件
     *
     * @param fileVOS
     * @param isDeleteLocal
     * @param isDeleteDB
     */
    public void deleteFileByFileVO(List<FileVO> fileVOS, boolean isDeleteLocal, boolean isDeleteDB) {
        if (CollectionUtil.isNotEmpty(fileVOS)) {
            deleteFileByURL(fileVOS.stream().filter(fileVO -> fileVO != null).map(fileVO -> fileVO.getUrl()).collect(Collectors.toList()), isDeleteLocal, isDeleteDB);
        }
    }

    /**
     * 更新文件
     *
     * @param newFileDTOS 新文件
     * @param oldFileVOS  原始文件
     */
    public void deleteFileForUpdate(List<FileDTO> newFileDTOS, List<FileVO> oldFileVOS) {
        List<String> newUrls = CollectionUtils.isEmpty(newFileDTOS) ? new ArrayList<>() : newFileDTOS.stream().filter(fileDTO -> fileDTO != null).map(fileDTO -> fileDTO.getUrl()).collect(Collectors.toList());
        List<String> oldUrls = CollectionUtils.isEmpty(oldFileVOS) ? new ArrayList<>() : oldFileVOS.stream().filter(fileVO -> fileVO != null).map(fileVO -> fileVO.getUrl()).collect(Collectors.toList());
        deleteFileForUpdateByURL(newUrls, oldUrls);
    }

    /**
     * 更新文件
     *
     * @param newUrls 新文件
     * @param oldUrls 原始文件
     */
    public void deleteFileForUpdateByURL(List<String> newUrls, List<String> oldUrls) {

        //删除数据库临时文件
        newUrls = new ArrayList<>(newUrls);
        deleteFileByURL(newUrls, false, true);

        if (!CollectionUtils.isEmpty(oldUrls)) {
            //删除本地文件和数据库文件
            oldUrls = new ArrayList<>(oldUrls);
            oldUrls.removeAll(newUrls);
            deleteFileByURL(oldUrls, true, true);
        }

    }

    /**
     * 删除文件
     *
     * @param urls
     * @param isDeleteLocal 是否删除本地文件
     * @param isDeleteDB    是否删除数据库中保存的记录
     */
    public void deleteFileByURL(List<String> urls, boolean isDeleteLocal, boolean isDeleteDB) {
        if (!CollectionUtils.isEmpty(urls)) {
            if (isDeleteDB) {
                uploadFileMapper.deleteBatchIds(urls);
            }
            if (clearLocalFile != null && clearLocalFile && isDeleteLocal) {
                urls.forEach(url -> {
                    if (url != null && url.contains("/upload/files")) {
                        File file = new File(filePath + url.split("/upload/files")[1]);
                        if (file.exists()) {
                            file.delete();
                        }
                    }
                });
            }
        }

    }

    /**
     * 清理垃圾图片
     */
    public void cleanFile() {
        List<UploadFile> rubbishFile = uploadFileMapper.findRubbishFile();
        log.info("===========================总共清理" + rubbishFile.size() + "张图片===========================");
        deleteFileByURL(rubbishFile.stream().map(file -> file.getUrl()).collect(Collectors.toList()), true, true);
    }
}
